package concurrent.threadscheduling;


import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程顺序流程控制。
 * 流程控制和顺序控制方法一样，都是用锁的通知，来实现线程间通信
 * @Author Antony
 * @Since 2018/5/8 23:05
 */
public class ThreadOrderControl {


    public static void main(String[] args) throws InterruptedException {

//        前两个方法都存在固有缺陷：
//        Join 需要线程都同时启动，sych使用多个锁来阻塞线程实现通知。但锁过多，性能低
//        orderedThreadABC_Join();
//        orderedThreadABC_sych();

        // 以下方式比较推荐。
        // 使用 公有的 volatile flag 变量来保证线程执行顺序
        // 然后在线程内 while循环判断 flag。如果不是当前线程的则使用 wait await 来暂停并且释放锁。
        // 执行完成后，flag 置为下一个线程的标志，然后执行 notifyAll  singalAll

//        orderedThreadABC_singal();
        orderedThreadABC_singal();
    }


    /**
     * 使用显示锁 Lock 来锁定，condition谓词来判断状态，以及 await singla 来实现线程间通知
     * 无需顺序启动。
     * 用 volatile 变量保存状态，
     */
    static volatile int lockFlag = 0;
    private static void orderedThreadABC_singal() throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        Condition c1 = lock.newCondition();

        Thread t1 = new Thread(() ->{
            System.out.println("A Start...");
            lock.lock();
            try {
                System.out.println("A Thread ......");
                Thread.sleep(1000);
                lockFlag = 1;
                c1.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });

        Thread t2 = new Thread(() -> {
            System.out.println("B Start...");
            lock.lock();
            try {
                while(lockFlag != 1){   //注意此处需要循环校验
                    c1.await();
                }
                System.out.println("B Thread ......");
                Thread.sleep(1000);
                lockFlag = 2;
                c1.signalAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });

        Thread t3 = new Thread(() -> {
            System.out.println("C Start...");
            lock.lock();
            try {
                while(lockFlag != 2){
                    c1.await();
                }
                System.out.println("C Thread ......");
                Thread.sleep(1000);

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });

        t3.start();
        Thread.sleep(2000);
        t2.start();
        Thread.sleep(2000);
        t1.start();
    }


    /**
     * 使用内置锁的 wait() 和 notify() 来实现线程间通知
     */
    static volatile String syncFlag = "A";
    private static void orderedThreadABC_notify(){
        Object lockObj = new Object();
        Thread t1 = new Thread(new SyncThread(lockObj,"A","B"));
        Thread t2 = new Thread(new SyncThread(lockObj,"B","C"));
        Thread t3 = new Thread(new SyncThread(lockObj,"C","A"));

        t3.start();
        t2.start();
        t1.start();
    }

    private static class SyncThread implements Runnable{
        Object lockObj;
        String current;
        String next;

        public SyncThread(Object lockObj, String current, String next) {
            this.lockObj = lockObj;
            this.current = current;
            this.next = next;
        }

        @Override
        public void run() {
            System.out.println(current+" Sync Start...");
            synchronized (lockObj){
                try {
                    while(!current.equals(syncFlag)){
                        lockObj.wait();
                    }
                    System.out.println(current + " Sync Thread Execute......");
                    Thread.sleep(2000);

                    syncFlag = next;        //执行完成后，flag置为下一个顺序要执行的线程标志，然后通知唤醒
                    lockObj.notifyAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }



    /**
     * 该方法存在问题
     * 必须要ABC顺序启动 才可以。不然就会出问题。
     * @throws InterruptedException
     */
    private static void orderedThreadABC_sych() throws InterruptedException {
        Object a = new Object();
        Object b = new Object();
        Object c = new Object();

        Thread t1 = new Thread(new ThreadABCDemo(a,b), "A");
        Thread t2 = new Thread(new ThreadABCDemo(c,a), "B");
        Thread t3 = new Thread(new ThreadABCDemo(b,c), "C");

        t3.start();
        t1.start();
        t2.start();

    }

    static class ThreadABCDemo implements Runnable{
        Object curr;
        Object next;

        public ThreadABCDemo(Object curr, Object next) {
            this.curr = curr;
            this.next = next;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " start...");
            synchronized (curr){
                synchronized (next){
                    System.out.println(Thread.currentThread().getName() + " Thread ......");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        }
    }

    /**
     * 该方法也存在问题，需要ABC同时启动。
     * 比如现在这种方式，就没法保证ABC顺序执行
     */
    private static void orderedThreadABC_Join() throws InterruptedException {

        Thread t1 = new Thread(() -> {
            System.out.println("A start...");
            System.out.println("A Thread......");
        });

        Thread t2 = new Thread(() -> {
            System.out.println("B start...");
            try {
                System.out.println("A Thread Alive=" + t1.isAlive());
                t1.join();
                System.out.println("B Thread......");

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread t3 = new Thread(() ->{
            System.out.println("C start...");
            try {
                System.out.println("B Thread Alive=" + t2.isAlive());
                t2.join();
                System.out.println("C Thread......");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        t1.start();
        Thread.sleep(2000);
        t3.start();
        Thread.sleep(2000);
        t2.start();

    }


}
